﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using NGIS.Data.Schema;

namespace NGIS
{
    namespace Model
    {
        public class ModelBehavior : IModelBehavior
        {
            private List<ModelDatasetItem> mModelDatasetItemList = new List<ModelDatasetItem>();
            private List<ModelState> mModelStateList = new List<ModelState>();
            private List<ModelStateTransition> mModelStateTransitionList = new List<ModelStateTransition>();

            private List<ModelParameter> mProcessParameters = new List<ModelParameter>();
            private List<ModelParameter> mControlParameters = new List<ModelParameter>();

            public bool loadFromXml(XmlElement pModelClassElement)
            {
			    XmlElement behaviorEle = (XmlElement)pModelClassElement.ChildNodes[1];

                //! Related Dataset
                XmlNodeList nlist_rds = behaviorEle.GetElementsByTagName("RelatedDatasets");
                if (nlist_rds.Count > 0)
                {
                    XmlElement relatedDatasetsEle = (XmlElement)nlist_rds[0];
                    for (int i = 0; i < relatedDatasetsEle.ChildNodes.Count; i++)
                    {
                        XmlElement childEle = (XmlElement)relatedDatasetsEle.ChildNodes[i];
                        ModelDatasetItem temp_item = new ModelDatasetItem();
                        temp_item.datasetName = childEle.GetAttribute("name");
                        temp_item.datasetItemDescription = childEle.GetAttribute("description");
                        string temp_type = childEle.GetAttribute("type");

                        if (temp_type == "internal")
                        {
                            temp_item.datasetItemType = EModelDatasetItemType.EMDIT_INTERNAL;
                            temp_item.externalId = "";
                            //! TODO
                            // parse udx schema, please consider memory control
                            UdxDatasetSchema pDataset = new UdxDatasetSchema(null, "UdxDeclaration");
                            pDataset.LoadFromXmlElement(childEle.FirstChild as XmlElement);
                            temp_item.datasetItem = pDataset;
                        }
                        else if (temp_type == "external")
                        {
                            temp_item.datasetItemType = EModelDatasetItemType.EMDIT_EXTERNAL;
                            temp_item.externalId = childEle.GetAttribute("externalId");
                            temp_item.datasetItem = null;
                        }
                        else if (temp_type == "raw")
                        {
                            temp_item.datasetItemType = EModelDatasetItemType.EMDIT_RAW;
                            temp_item.externalId = "";
                            temp_item.datasetItem = null;
                        }
                        addModelDatasetItem(ref temp_item);
                    }
                }
                
                //! States
                XmlNodeList nlist_states = behaviorEle.GetElementsByTagName("StateGroup");
                if (nlist_states.Count > 0)
                {
                    XmlElement stateGroupEle = (XmlElement)nlist_states[0];
                    XmlElement states_ele = (XmlElement)stateGroupEle.FirstChild;
                    for (int i = 0; i < states_ele.ChildNodes.Count; i++)
                    {
                        XmlElement childEle = (XmlElement)states_ele.ChildNodes[i];
                        ModelState temp_state = new ModelState();
                        temp_state.stateId = childEle.GetAttribute("id");
                        temp_state.stateName = childEle.GetAttribute("name");
                        temp_state.stateDecription = childEle.GetAttribute("description");
                        string temp_type = childEle.GetAttribute("type");
                        if (temp_type == "basic")
                        {
                            temp_state.stateType = EModelStateType.EMST_BASIC;
                        }
                        else if (temp_type == "group")
                        {
                            temp_state.stateType = EModelStateType.EMST_GROUP;
                        }
                        //////////////////////////////////////////////////////////////////////////
                        temp_state.modelEvents = new List<ModelEvent>();
                        for (int iEvent = 0; iEvent < childEle.ChildNodes.Count; iEvent++)
                        {
                            XmlElement childEle1 = (XmlElement)childEle.ChildNodes[iEvent];
                            ModelEvent modelEvent = new ModelEvent();

                            modelEvent.eventName = childEle1.GetAttribute("name");
                            modelEvent.eventDescription = childEle1.GetAttribute("description");
                            string eventType = childEle1.GetAttribute("type");
                            if (eventType == "response")
                            {
                                modelEvent.eventType = EModelEventType.EMET_RESPONSE;
                            }
                            else if (eventType == "noresponse")
                            {
                                modelEvent.eventType = EModelEventType.EMET_NORESPONSE;
                            }
                            else if (eventType == "control")
                            {
                                modelEvent.eventType = EModelEventType.EMET_CONTROL;
                            }
                            XmlElement param_ele = (XmlElement)childEle1.FirstChild;
                            if (param_ele.HasAttribute("description"))
                                modelEvent.parameterDescription = param_ele.GetAttribute("description");
                            else
                                modelEvent.parameterDescription = "";
                            modelEvent.datasetReference = param_ele.GetAttribute("datasetReference");

                            string optionalStr = childEle1.GetAttribute("optional");
                            if (optionalStr == "")
                                modelEvent.optional = false;
                            else
                                modelEvent.optional = Convert.ToBoolean(optionalStr);

                            
                            temp_state.modelEvents.Add(modelEvent);
                        }
                        addModelState(ref temp_state);
                    }

                    XmlElement state_transition_ele = (XmlElement)stateGroupEle.ChildNodes[1];
                    for (int iStateTran = 0; iStateTran < state_transition_ele.ChildNodes.Count; iStateTran++)
                    {
                        XmlElement childEle = (XmlElement)state_transition_ele.ChildNodes[iStateTran];
                        string fromId = childEle.GetAttribute("from");
                        string toId = childEle.GetAttribute("to");
                        addModelStateTransition(fromId, toId);
                    }
                }
                
                //! Parameters
                XmlNodeList nlist_paramters = behaviorEle.GetElementsByTagName("Parameters");
                if (nlist_paramters.Count > 0)
                {
                    XmlElement parametersEle = (XmlElement)nlist_paramters[0];
                    XmlElement ppEle = (XmlElement)parametersEle.ChildNodes[0];
                    foreach (XmlElement ele in ppEle.ChildNodes)
                    {
                        ModelParameter pp = new ModelParameter();
                        pp.Key = ele.GetAttribute("key");
                        pp.Description = ele.GetAttribute("description");
                        pp.DefaultValue = ele.GetAttribute("defaultValue");
                        this.mProcessParameters.Add(pp);
                    }

                    XmlElement cpEle = (XmlElement)parametersEle.ChildNodes[1];
                    foreach (XmlElement ele in cpEle.ChildNodes)
                    {
                        ModelParameter cp = new ModelParameter();
                        cp.Key = ele.GetAttribute("key");
                        cp.Description = ele.GetAttribute("description");
                        cp.DefaultValue = ele.GetAttribute("defaultValue");
                        this.mProcessParameters.Add(cp);
                    }
                }

			    return true;
		    }

			public bool formatToXml(XmlElement pModelClassElement)
			{
				XmlDocument doc = pModelClassElement.OwnerDocument;
				XmlElement behaviorEle = doc.CreateElement("Behavior");

				XmlElement relatedDatasetsEle = doc.CreateElement("RelatedDatasets");
				XmlElement stateGroupEle = doc.CreateElement("StateGroup");
                XmlElement parameterEle = doc.CreateElement("Parameters");

				for (int i=0; i<mModelDatasetItemList.Count; i++)
				{
					ModelDatasetItem temp_item = mModelDatasetItemList[i];
					XmlElement temp_ele = doc.CreateElement("DatasetItem");
					temp_ele.SetAttribute("name", temp_item.datasetName);
					if (temp_item.datasetItemType == EModelDatasetItemType.EMDIT_EXTERNAL)
					{
						temp_ele.SetAttribute("type", "external");
						temp_ele.SetAttribute("externalId", temp_item.externalId);
						temp_ele.SetAttribute("description", temp_item.datasetItemDescription);
					}
					else if (temp_item.datasetItemType == EModelDatasetItemType.EMDIT_INTERNAL)
					{
						temp_ele.SetAttribute("type", "internal");
						temp_ele.SetAttribute("description", temp_item.datasetItemDescription);
						if (temp_item.datasetItem != null)
                        {
							temp_item.datasetItem.FormatToXmlElement(ref temp_ele);
                        }
					}
					else if (temp_item.datasetItemType == EModelDatasetItemType.EMDIT_RAW)
					{
						temp_ele.SetAttribute("type", "raw");
						temp_ele.SetAttribute("description", temp_item.datasetItemDescription);
					}

					relatedDatasetsEle.AppendChild(temp_ele);
				}

				XmlElement states_ele = doc.CreateElement("States");
				stateGroupEle.AppendChild(states_ele);
				for (int i=0; i<mModelStateList.Count; i++)
				{
					ModelState temp_state = mModelStateList[i];
					XmlElement temp_state_ele = doc.CreateElement("State");
					temp_state_ele.SetAttribute("id", temp_state.stateId);
					temp_state_ele.SetAttribute("name", temp_state.stateName);
					if (temp_state.stateType == EModelStateType.EMST_BASIC)
						temp_state_ele.SetAttribute("type", "basic");
					else if (temp_state.stateType == EModelStateType.EMST_GROUP)
						temp_state_ele.SetAttribute("type", "group");
					else
						temp_state_ele.SetAttribute("type", "unknown");
					temp_state_ele.SetAttribute("description", temp_state.stateDecription);

					//////////////////////////////////////////////////////////////////////////
					for (int iEvent = 0; iEvent<temp_state.modelEvents.Count; iEvent++)
					{
						ModelEvent temp_event = temp_state.modelEvents[iEvent];
						EModelEventType eventType = temp_event.eventType;
						XmlElement event_ele = doc.CreateElement("Event");
						event_ele.SetAttribute("name", temp_event.eventName);
						if (eventType == EModelEventType.EMET_RESPONSE)
						{
							event_ele.SetAttribute("type", "response");
							XmlElement param_ele = doc.CreateElement("ResponseParameter");
							param_ele.SetAttribute("datasetReference", temp_event.datasetReference);
							param_ele.SetAttribute("description", temp_event.parameterDescription);
							event_ele.AppendChild(param_ele);
						}
						else if (eventType == EModelEventType.EMET_NORESPONSE)
						{
							event_ele.SetAttribute("type", "noresponse");
							XmlElement param_ele = doc.CreateElement("DispatchParameter");
							param_ele.SetAttribute("datasetReference", temp_event.datasetReference);
							param_ele.SetAttribute("description", temp_event.parameterDescription);
							event_ele.AppendChild(param_ele);
						}
						else if (eventType == EModelEventType.EMET_CONTROL)
						{
							event_ele.SetAttribute("type", "control");
							XmlElement param_ele = doc.CreateElement("ControlParameter");
							param_ele.SetAttribute("datasetReference", temp_event.datasetReference);
							param_ele.SetAttribute("description", temp_event.parameterDescription);
							event_ele.AppendChild(param_ele);
						}
						event_ele.SetAttribute("optional", temp_event.optional.ToString());
						event_ele.SetAttribute("description", temp_event.eventDescription);

						temp_state_ele.AppendChild(event_ele);
					}
					
					states_ele.AppendChild(temp_state_ele);
				}

				XmlElement state_transition_ele = doc.CreateElement("StateTransitions");
				stateGroupEle.AppendChild(state_transition_ele);
                foreach (var it in mModelStateTransitionList)
                {
					string temp_from = it.fromState.stateId;
					string temp_to = it.toState.stateId;
					XmlElement temp_ele = doc.CreateElement("Add");
					temp_ele.SetAttribute("from", temp_from);
					temp_ele.SetAttribute("to", temp_to);

					state_transition_ele.AppendChild(temp_ele);
				}

                XmlElement ppsEle = doc.CreateElement("ProcessParameters");
                foreach (ModelParameter mp in this.mProcessParameters)
                {
                    XmlElement ppEle = doc.CreateElement("Add");
                    ppEle.SetAttribute("key", mp.Key);
                    ppEle.SetAttribute("description", mp.Description);
                    ppEle.SetAttribute("defaultValue", mp.DefaultValue);
                    ppsEle.AppendChild(ppEle);
                }
                parameterEle.AppendChild(ppsEle);

                XmlElement cpsEle = doc.CreateElement("ControlParameters");
                foreach (ModelParameter mp in this.mControlParameters)
                {
                    XmlElement ppEle = doc.CreateElement("Add");
                    ppEle.SetAttribute("key", mp.Key);
                    ppEle.SetAttribute("description", mp.Description);
                    ppEle.SetAttribute("defaultValue", mp.DefaultValue);
                    cpsEle.AppendChild(ppEle);
                }
                parameterEle.AppendChild(cpsEle);

				behaviorEle.AppendChild(relatedDatasetsEle);
				behaviorEle.AppendChild(stateGroupEle);
                behaviorEle.AppendChild(parameterEle);

				pModelClassElement.AppendChild(behaviorEle);
				return true;
			}

            ///////////////////////////////////////////////////////////////////

            public bool addModelDatasetItem(ref ModelDatasetItem pDataset)
            {
                for (int i = 0; i < mModelDatasetItemList.Count; i++)
                {
                    if (mModelDatasetItemList[i].datasetName == pDataset.datasetName)
                    {
                        return false;
                    }
                }
                mModelDatasetItemList.Add(pDataset);
                return true;
            }

            public bool removeModelDatasetItem(ref ModelDatasetItem pDataset)
            {
                if (mModelDatasetItemList.Contains(pDataset))
                {
                    mModelDatasetItemList.Remove(pDataset);
                    return true;
                }
				return false;
            }

            public int getModelDatasetItemCount()
            {
                return mModelDatasetItemList.Count;
            }

            public bool getModelDatasetItem(int idx, ref ModelDatasetItem pDataset)
            {
                if (idx < 0 || idx >= mModelDatasetItemList.Count) return false;
                pDataset = mModelDatasetItemList[idx];
                return true;
            }

            public bool getModelDatasetItem(string pName, ref ModelDatasetItem pDataset)
            {
                for (int i = 0; i < mModelDatasetItemList.Count; i++)
                {
                    if (mModelDatasetItemList[i].datasetName == pName)
                    {
                        pDataset = mModelDatasetItemList[i];
                        return true;
                    }
                }
                return false;
            }

            public bool updateModelDatasetItem(int idx, ref ModelDatasetItem pDataset)
            {
                if (idx < 0 || idx >= mModelDatasetItemList.Count) return false;
                mModelDatasetItemList[idx] = pDataset;
                return true;
            }

            public bool addModelState(ref ModelState pState)
            {
                for (int i = 0; i < mModelStateList.Count; i++)
                {
                    if (mModelStateList[i].stateId == pState.stateId)
                    {
                        return false;
                    }
                }
                mModelStateList.Add(pState);
                return true;
            }

            public bool removeModelState(ref ModelState pState)
            {
                if (mModelStateList.Contains(pState))
                {
                    mModelStateList.Remove(pState);
                }
				return false;
            }

            public int getModelStateCount()
            {
                return mModelStateList.Count;
            }

            public bool getModelState(int idx, ref ModelState pState)
            {
                if (idx < 0 || idx >= mModelStateList.Count) return false;
                pState = mModelStateList[idx];
                return true;
            }

            public bool getModelState(string pStateId, ref ModelState pState)
            {
                for (int i = 0; i < mModelStateList.Count; i++)
                {
                    if (mModelStateList[i].stateId == pStateId)
                    {
                        pState = mModelStateList[i];
                        return true;
                    }
                }
                return false;
            }

            public bool updateModelState(int idx, ref ModelState pState)
            {
                if (idx < 0 || idx >= mModelStateList.Count) return false;
                mModelStateList[idx] = pState;
                return true;
            }

            public bool updateModelState(string pStateId, ref ModelState pState)
            {
                for (int i = 0; i < mModelStateList.Count; i++)
                {
                    if (mModelStateList[i].stateId == pStateId)
                    {
                        mModelStateList[i] = pState;
                        return true;
                    }
                }
                return false;
            }

            public bool addModelStateTransition(string pFromStateId, string pToStateId)
            {
                ModelState fromState = new ModelState();
                ModelState toState = new ModelState();
                bool flag1 = getModelState(pFromStateId, ref fromState);
                bool flag2 = getModelState(pToStateId, ref toState);
                if (flag1 && flag2)
                {
                    for (int i = 0; i < mModelStateTransitionList.Count; i++)
                    {
                        if (mModelStateTransitionList[i].fromState.stateId == pFromStateId &&
                            mModelStateTransitionList[i].toState.stateId == pToStateId)
                        {
                            return false;
                        }
                    }
                    ModelStateTransition temp_transition;
                    temp_transition.fromState = fromState;
                    temp_transition.toState = toState;
                    mModelStateTransitionList.Add(temp_transition);
                    return true;
                }
                return false;
            }

            public bool addModelStateTransition(ref ModelState pFromState, ref ModelState pToState)
            {
                return addModelStateTransition(pFromState.stateId, pToState.stateId);
            }

            public bool removeModelStateTransition(string pFromStateId, string pToStateId)
            {
                for (int i = 0; i < mModelStateTransitionList.Count; i++)
				{
                    ModelStateTransition it = mModelStateTransitionList[i];
                    if (it.fromState.stateId == pFromStateId && it.toState.stateId == pToStateId)
					{
						mModelStateTransitionList.Remove(it);
						return true;
					}
				}
				return false;
            }

            public bool removeModelStateTransition(ref ModelState pFromState, ref ModelState pToState)
            {
                return removeModelStateTransition(pFromState.stateId, pToState.stateId);
            }

            public int getModelStateTransitionCount()
            {
                return mModelStateTransitionList.Count;
            }

            public bool getModelStateTransition(int idx, ref ModelState pFromState, ref ModelState pToState)
            {
				if (idx<0 || idx>= mModelStateTransitionList.Count) return false;
                ModelStateTransition it = mModelStateTransitionList[idx];
				pFromState = it.fromState;
				pToState = it.toState;
				return true;
            }

            public bool getModelStateTransition(int idx, ref ModelStateTransition pStateTransition)
            {
                if (idx < 0 || idx >= mModelStateTransitionList.Count) return false;
                pStateTransition = mModelStateTransitionList[idx];
                return true;
            }

            public bool existModelStatetTransition(string pFromStateId, string pToStateId)
            {
                for (int i = 0; i < mModelStateTransitionList.Count; i++)
				{
                    ModelStateTransition it = mModelStateTransitionList[i];
					if (it.fromState.stateId == pFromStateId && it.toState.stateId == pToStateId)
					{
						return true;
					}
				}
				return false;
            }

            public bool existModelStatetTransition(ref ModelState pFromState, ref ModelState pToState)
            {
                return existModelStatetTransition(pFromState.stateId, pToState.stateId);
            }

            public bool updateModelStateTransition(int idx, string pFromStateId, string pToStateId)
            {
				if (idx<0 || idx>= mModelStateTransitionList.Count) return false;
                ModelStateTransition it = new ModelStateTransition();
                getModelStateTransition(idx, ref it);

                removeModelStateTransition(ref it.fromState, ref it.toState);
				addModelStateTransition(pFromStateId, pToStateId);
				return true;
            }

            public bool compareOther(IModelBehavior pBehavior, ref string obj, ref string name)
            {
                if (mModelDatasetItemList.Count != pBehavior.getModelDatasetItemCount())
                {
                    obj = "RelatedDatasets";
                    name = "DatasetItemCount";
                    return false;
                }
                for (int i = 0; i < mModelDatasetItemList.Count; i++)
                {
                    ModelDatasetItem temp_item1 = mModelDatasetItemList[i];
                    ModelDatasetItem temp_item2 = new ModelDatasetItem(); ;
                    pBehavior.getModelDatasetItem(i, ref temp_item2);
                    if (temp_item1.compareOther(temp_item2) == false)
                    {
                        obj = "DatasetItem";
                        name = temp_item1.datasetName + "<->" + temp_item2.datasetName;
                        return false;
                    }
                }
                if (mModelStateList.Count != pBehavior.getModelStateCount())
                {
                    obj = "States";
                    name = "StateCount";
                    return false;
                }
                for (int i = 0; i < mModelStateList.Count; i++)
                {
                    ModelState temp_state1 = mModelStateList[i];
                    ModelState temp_state2 = new ModelState();
                    pBehavior.getModelState(i, ref temp_state2);
                    if (temp_state1.compareOther(temp_state2) == false)
                    {
                        obj = "State";
                        name = temp_state1.stateName + "<->" + temp_state2.stateName;
                        return false;
                    }
                }
                if (mModelStateTransitionList.Count != pBehavior.getModelStateTransitionCount())
                {
                    obj = "StateTransitions";
                    name = "StateTransitionCount";
                    return false;
                }
                for (int i = 0; i < mModelStateTransitionList.Count; i++)
                {
                    ModelStateTransition temp_trans1 = mModelStateTransitionList[i];
                    ModelStateTransition temp_trans2 = new ModelStateTransition();
                    pBehavior.getModelStateTransition(i, ref temp_trans2);
                    if (temp_trans1.compareOther(temp_trans2) == false)
                    {
                        obj = "StateTransition";
                        name = temp_trans1.fromState.stateId + "<->" + temp_trans2.fromState.stateId;
                        return false;
                    }
                }
                return true;
            }


            public bool addProcessParameter(ModelParameter processparameter)
            {
                if (!this.mProcessParameters.Contains(processparameter)) this.mProcessParameters.Add(processparameter);
                return true;
            }

            public bool getProcessParameter(string key, ref ModelParameter parameter)
            {
                for (int i = 0; i < this.mProcessParameters.Count; i++)
                {
                    if (this.mProcessParameters[i].Key == key)
                    {
                        parameter = this.mProcessParameters[i];
                        return true;
                    }
                }
                return false;
            }

            public int getProcessParameterCount()
            {
                return this.mProcessParameters.Count;
            }

            public bool removeProcessParameter(ModelParameter processparameter)
            {
                return this.mProcessParameters.Remove(processparameter);
            }

            public bool updateProcessParameter(ModelParameter processparameter)
            {
                for (int i = 0; i < this.mProcessParameters.Count; i++)
                {
                    if (this.mProcessParameters[i].Key == processparameter.Key)
                    {
                        this.mProcessParameters[i] = processparameter;
                        return true;
                    }
                }
                return false;
            }

            public bool addControlParameter(ModelParameter controlparameter)
            {
                if (!this.mControlParameters.Contains(controlparameter)) this.mControlParameters.Add(controlparameter);
                return true;
            }

            public bool getControlParameter(string key, ref ModelParameter parameter)
            {
                for (int i = 0; i < this.mControlParameters.Count; i++)
                {
                    if (this.mControlParameters[i].Key == key)
                    {
                        parameter = this.mControlParameters[i];
                        return true;
                    }
                }
                return false;
            }

            public int getControlParameterCount()
            {
                return this.mControlParameters.Count;
            }

            public bool removeControlParameter(ModelParameter controlparameter)
            {
                return this.mControlParameters.Remove(controlparameter);
            }

            public bool updateControlParameter(ModelParameter controlparameter)
            {
                for (int i = 0; i < this.mControlParameters.Count; i++)
                {
                    if (this.mControlParameters[i].Key == controlparameter.Key)
                    {
                        this.mControlParameters[i] = controlparameter;
                        return true;
                    }
                }
                return false;
            }
        }
    }
}